package org.lanyu.springainovel.common.config;

import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.security.MessageDigest;

/**
 * 自定义密码编码器
 * 使用用户名作为盐值，确保同一个用户名的同一个密码生成固定的加密密码
 */
@Component
public class CustomPasswordEncoder implements PasswordEncoder {

    private static final String DEFAULT_USERNAME = "admin"; // 默认用户名，用于没有用户名的情况
    private static final int STRENGTH = 10; // BCrypt强度

    /**
     * 生成基于用户名的固定盐值
     *
     * @param username 用户名
     * @return 固定盐值（22个字符，符合BCrypt要求）
     */
    private String generateFixedSalt(String username) {
        try {
            // 使用SHA-256哈希用户名
            java.security.MessageDigest digest = java.security.MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(username.getBytes(java.nio.charset.StandardCharsets.UTF_8));

            // 将哈希值转换为Base64字符串
            String base64Hash = java.util.Base64.getEncoder().encodeToString(hash);

            // BCrypt盐值只能包含./A-Za-z0-9字符，我们需要替换Base64中的特殊字符
            String bcryptChars = base64Hash
                    .replace('+', '.')  // 将+替换为.
                    .replace('/', '.')  // 将/替换为.
                    .replace('=', 'a'); // 将=替换为a

            // 确保长度为22个字符
            String fixedSalt = bcryptChars.substring(0, 22);
            return fixedSalt;
        } catch (Exception e) {
            e.printStackTrace();// 如果生成固定盐值失败，使用默认的随机盐值
            return BCrypt.gensalt(STRENGTH).substring(7); // 去掉"$2a$10$"前缀
        }
    }

    @Override
    public String encode(CharSequence rawPassword) {
        // 默认使用admin作为盐值，因为这是默认的管理员用户名
        String saltedPassword = DEFAULT_USERNAME + "{" + rawPassword + "}";
        // 使用固定的盐值，确保同一用户名的同一密码生成固定的加密密码
        String salt = "$2a$" + STRENGTH + "$" + generateFixedSalt(DEFAULT_USERNAME);
        String encodedPassword = BCrypt.hashpw(saltedPassword, salt);

        // 添加调试日志
        System.out.println("CustomPasswordEncoder.encode:");
        System.out.println("  Default Username: " + DEFAULT_USERNAME);
        System.out.println("  RawPassword: " + rawPassword);
        System.out.println("  SaltedPassword: " + saltedPassword);
        System.out.println("  Salt: " + salt);
        System.out.println("  EncodedPassword: " + encodedPassword);

        return encodedPassword;
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        // 默认使用admin作为盐值，因为这是默认的管理员用户名
        String saltedPassword = DEFAULT_USERNAME + "{" + rawPassword + "}";
        boolean result = BCrypt.checkpw(saltedPassword, encodedPassword);

        // 添加调试日志
        System.out.println("CustomPasswordEncoder.matches:");
        System.out.println("  Default Username: " + DEFAULT_USERNAME);
        System.out.println("  RawPassword: " + rawPassword);
        System.out.println("  SaltedPassword: " + saltedPassword);
        System.out.println("  EncodedPassword: " + encodedPassword);
        System.out.println("  Match result: " + result);

        return result;
    }

    /**
     * 使用用户名作为盐值进行密码编码
     * 这样同一个用户名的同一个密码会生成固定的加密密码
     *
     * @param rawPassword 明文密码
     * @param username    用户名（作为盐值）
     * @return 加密后的密码
     */
    public String encodeWithUsernameSalt(CharSequence rawPassword, String username) {

        // 将用户名和原始密码组合，形成加盐的密码（与matchesWithUsernameSalt保持一致）
        String saltedPassword = rawPassword + username;

        try {
            // 生成基于用户名的固定盐值
            String fixedSalt = generateFixedSalt(username);

            // 创建完整的盐值前缀
            String salt = "$2a$" + STRENGTH + "$" + fixedSalt;

            // 使用BCrypt.hashpw加密密码
            String encodedPassword = BCrypt.hashpw(saltedPassword, salt);
            return encodedPassword;
        } catch (Exception e) {
            e.printStackTrace();
            // 如果生成固定盐值失败，使用默认的随机盐值
            return BCrypt.hashpw(saltedPassword, BCrypt.gensalt(STRENGTH));
        }
    }

    /**
     * 使用用户名作为盐值验证密码
     *
     * @param rawPassword     原始密码
     * @param encodedPassword 加密后的密码
     * @param username        用户名（作为盐值）
     * @return 是否匹配
     */
    public boolean matchesWithUsernameSalt(String rawPassword, String encodedPassword, String username) {
        // 将用户名和原始密码组合，形成加盐的密码
        String saltedPassword = rawPassword + username;

        try {
            // 使用BCrypt.checkpw验证密码
            boolean result = BCrypt.checkpw(saltedPassword, encodedPassword);
            return result;
        } catch (Exception e) {
            System.out.println("密码验证异常: " + e.getMessage());
            e.printStackTrace();
            System.out.println("=== CustomPasswordEncoder.matchesWithUsernameSalt 结束 ===");
            return false;
        }
    }
}